package com.caochenlei.config;

import com.alibaba.fastjson.JSON;
import eu.bitwalker.useragentutils.UserAgent;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * @author 胡甫圣
 * @Title: 日志切面
 * @Package
 * @Description:
 * @date 2021/1/5 21:40
 */
@Aspect/*表明是一个切面类*/
@Component /*将当前类注入到Spring容器内*/
@Slf4j
public class LogAspect {
    /** 进入方法时间戳 */
    private Long startTime;
    /** 方法结束时间戳(计时) */
    private Long endTime;

    /**
     * 切入点，其中execution用于使用切面的连接点
     */
    @Pointcut("execution(public * com.caochenlei.controller.*.*(..))")
    public void LogAspect() {
    }

    /*在方法前执行*/
    @Before("LogAspect()")
    public void doBefore(JoinPoint joinPoint) {
        System.out.println("doBefore");
    }

    /*在方法执行后返回结果后执行*/
    @AfterReturning(value ="LogAspect()", returning = "keys")
    public void doAfterReturning(JoinPoint joinPoint, Object keys) {
        System.out.println("doAfterReturning");
        log.info("方法执行完成返回结果:{}",JSON.toJSONString(keys));
    }

    /*在方法后执行*/
    @After("LogAspect()")
    public void doAfter(JoinPoint joinPoint) {
        System.out.println("doAfter");
    }

    /**
     * 在方法执行过程中抛出异常的时候执行
     *
     * @param joinPoint 封装了SpringAop中切面方法的信息
     *                  Signature getSignature();	获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息
     *                  Object[] getArgs();	获取传入目标方法的参数对象
     *                  Object getTarget();	获取被代理的对象
     *                  Object getThis();	获取代理对象
     */
    @AfterThrowing("LogAspect()")
    public void deAfterThrowing(JoinPoint joinPoint) {
        System.out.println("deAfterThrowing");
    }

    /**
     * 环绕通知，就是可以在执行前后都使用
     * 这个方法参数必须为ProceedingJoinPoint，proceed()方法就是被切面的方法，
     * 上面四个方法可以使用JoinPoint，
     * JoinPoint包含了类名，被切面的方法名，参数等信息
     *
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("LogAspect()")
    public Object deAround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("deAround");

        // 获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // 从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
        // 请求的参数
        Map<String, String> rtnMap = converMap(request.getParameterMap());
        // 将参数所在的数组转换成json
        String params = JSON.toJSONString(rtnMap);

        // 下面两个数组中，参数值和参数名的个数和位置是一一对应的。
        // 参数值
        Object[] args = joinPoint.getArgs();
        // 获取连接点的方法签名对象
        Signature signature = joinPoint.getSignature();
        log.info("执行方法:{},传入参数：{}",signature,params);
        // 打印请求的内容
        startTime = System.currentTimeMillis();
        log.info("请求开始时间：{}", LocalDateTime.now());
        log.info("请求Url : {}", request.getRequestURL().toString());
        log.info("请求方式 : {}", request.getMethod());
        log.info("请求ip : {}", request.getRemoteAddr());
        log.info("请求内容类型 : {}", request.getContentType());
        log.info("请求参数 : {}", Arrays.toString(joinPoint.getArgs()));
        // 系统信息
        // 获取请求头中的User-Agent
        UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
        log.info("浏览器 : {}", userAgent.getBrowser().toString());
        log.info("浏览器版本 : {}", userAgent.getBrowserVersion());
        log.info("操作系统: {}", userAgent.getOperatingSystem().toString());
        return joinPoint.proceed();
    }

    /**
     * 转换request 请求参数
     *
     * @param paramMap request获取的参数数组
     */
    public Map<String, String> converMap(Map<String, String[]> paramMap) {
        Map<String, String> rtnMap = new HashMap<String, String>();
        for (String key : paramMap.keySet()) {
            rtnMap.put(key, paramMap.get(key)[0]);
        }
        return rtnMap;
    }
}
